#include "stdafx.h"
#include "DefaultGUIStyle.h"

#include "Timer.h"
#include "Filesystem.h"
#include "Exception.h"
#include "Engine.h"

namespace Cvekas {

// TODO: document how layers are used
// TODO: unify fading speed, colors

DefaultGUIStyle::DefaultGUIStyle(SpriteBatchPtr sprite_batch, uint layer)
: sprite_batch(sprite_batch), layer(layer)
{
	Engine e;
	TextureManagerPtr texture_manager = e.getVideoDevice()->getTextureManager();

	// Find or load default gui texture
	if(texture_manager->exists(DEFAULT_GUI_TEXTURE_NAME))
		desc.texture = texture_manager->getTexture2D(DEFAULT_GUI_TEXTURE_NAME);
	else
		desc.texture = texture_manager->loadTexture2D(DEFAULT_GUI_TEXTURE_NAME, 
			Filesystem::redirect(DEFAULT_GUI_TEXTURE_FILENAME));

	desc.font = BitmapFontPtr(new BitmapFont(sprite_batch, Filesystem::redirect(DEFAULT_GUI_FONT_FILENAME), layer+4));

	desc.src_button_up = RectF(0.0f, 0.0f, 256.0f, 64.0f);
	desc.src_button_down = RectF(0.0f, 64.0f, 256.0f, 64.0f);
	desc.src_switch_up = desc.src_button_up;
	desc.src_switch_down = desc.src_button_down;
	desc.src_slider = RectF(0.0f, 128.0f, 256.0f, 32.0f);
	desc.src_slider_knob_up = RectF(0.0f, 160.0f, 16.0f, 32.0f);
	desc.src_slider_knob_down = RectF(16.0f, 160.0f, 16.0f, 32.0f);
	desc.src_pointer = RectF(0.0f, 193.0f, 32.0f, 32.0f);

	desc.src_selector = desc.src_button_up;
	desc.src_selector_prev_up = RectF(34.0f, 160.0f, 32.0f, 64.0f);
	desc.src_selector_prev_down = RectF(102.0f, 160.0f, 32.0f, 64.0f);
	desc.src_selector_next_up = RectF(68.0f, 160.0f, 32.0f, 64.0f);
	desc.src_selector_next_down = RectF(136.0f, 160.0f, 32.0f, 64.0f);

	desc.text_color = Color(0, 0, 0);

	assert(validateDesc());
}

DefaultGUIStyle::DefaultGUIStyle(Cvekas::SpriteBatchPtr sprite_batch, DefaultGUIStyleDesc &desc, uint layer)
: sprite_batch(sprite_batch), layer(layer)
{
	this->desc = desc;
	if(!validateDesc())
		throw EXCEPTION("DefaultGUIStyleDesc invalid!");
}

DefaultGUIStyle::~DefaultGUIStyle()
{
}

bool DefaultGUIStyle::hitButton(const Vector2& pos, const std::string& text)
{
	RectF button(pos, desc.src_button_down.size());
	return button.collidePoint(mouse_pos);
}

bool DefaultGUIStyle::hitSwitch(const Vector2& pos, const std::string& text)
{
	RectF switch_(pos, desc.src_switch_down.size());
	return switch_.collidePoint(mouse_pos);
}

float DefaultGUIStyle::hitSlider(const Vector2& pos)
{
	RectF slider(pos, desc.src_slider.size());
	if(!slider.collidePoint(mouse_pos))
		return -1.0f;

	return (mouse_pos.x - pos.x) / desc.src_slider.width;
}

bool DefaultGUIStyle::hitSelector(const Vector2& pos, const std::vector<std::string>& selections, uint& button)
{
	button = 0;
	RectF selector(pos, desc.src_selector.size());
	if(!selector.collidePoint(mouse_pos))
		return false;

	RectF prev(pos, desc.src_selector_prev_up.size());
	RectF next(pos.x + (desc.src_selector.width - desc.src_selector_prev_up.width), pos.y,
		desc.src_selector_prev_up.width, desc.src_selector_prev_up.height);

	// Both can't be true
	assert(!(prev.collidePoint(mouse_pos) && next.collidePoint(mouse_pos)));

	if(prev.collidePoint(mouse_pos))
		button = 1;
	if(next.collidePoint(mouse_pos))
		button = 2;

	return true;
}

void DefaultGUIStyle::drawLabel(const Vector2& pos, const std::string& text)
{
	desc.font->draw(pos, text, desc.text_color);
}

void DefaultGUIStyle::drawButton(const Cvekas::Vector2& pos, const std::string& text, bool mouse_over, bool pressed)
{
	uint id = GUI::hashWidget(GUI::WIDGET_BUTTON, pos);
	
	if(!cache.exists(id))
	{
		WidgetStyleState dummy;
		dummy.blend = 0.0f;
		cache.set(dummy, id);
	}
	
	float& blend_modifier = cache.at(id).blend;
	
	if(mouse_over)
	{
		blend_modifier += Timer::getTimeScale()* 5.0f;
		if(blend_modifier > 1.0f)
			blend_modifier = 1.0f;
	}
	else
	{
		blend_modifier -= Timer::getTimeScale()* 5.0f;
		if(blend_modifier < 0.0f)
			blend_modifier = 0.0f;
	}

	sprite_batch->draw(desc.texture, layer+1, desc.src_button_up, pos, Color(255, 255, 255, 255));
	sprite_batch->draw(desc.texture, layer+2, desc.src_button_down, pos, Color(255, 255, 255, int(blend_modifier * 255.0f)));

	float text_width = desc.font->getWidth(text);
	float text_height = desc.font->getHeight();

	Vector2 text_pos(ceilf((desc.src_button_down.width - text_width) / 2.0f), ceilf((desc.src_button_down.height - text_height) / 2.0f));
	text_pos += pos;

	desc.font->draw(text_pos, text, desc.text_color);
}

void DefaultGUIStyle::drawSwitch(const Cvekas::Vector2 &pos, const std::string &text, bool mouse_over, bool state)
{
	uint id = GUI::hashWidget(GUI::WIDGET_SWITCH, pos);
	
	if(!cache.exists(id))
	{
		WidgetStyleState dummy;
		dummy.blend = 0.0f;
		cache.set(dummy, id);
	}
	
	float& blend_modifier = cache.at(id).blend;
	
	if(mouse_over)
	{
		blend_modifier += Timer::getTimeScale()* 5.0f;
		if(blend_modifier > 1.0f)
			blend_modifier = 1.0f;
	}
	else
	{
		blend_modifier -= Timer::getTimeScale()* 5.0f;
		if(blend_modifier < 0.0f)
			blend_modifier = 0.0f;
	}
	
	if(!state)
	{
		sprite_batch->draw(desc.texture, layer+1, desc.src_button_up, pos, Color(255, 255, 255, 255));
		sprite_batch->draw(desc.texture, layer+2, desc.src_button_down, pos, Color(255, 255, 255, int(blend_modifier * 64.0f)));
	}
	else
	{
		sprite_batch->draw(desc.texture, layer+1, desc.src_button_down, pos, Color(255, 255, 255, 255));
		sprite_batch->draw(desc.texture, layer+2, desc.src_button_up, pos, Color(255, 255, 255, int(blend_modifier * 64.0f)));
	}

	float text_width = desc.font->getWidth(text);
	float text_height = desc.font->getHeight();

	Vector2 text_pos(ceilf((desc.src_button_down.width - text_width) / 2.0f), ceilf((desc.src_button_down.height - text_height) / 2.0f));
	text_pos += pos;

	desc.font->draw(text_pos, text, desc.text_color);
}

void DefaultGUIStyle::drawSlider(const Cvekas::Vector2 &pos, bool mouse_over, float state)
{
	uint id = GUI::hashWidget(GUI::WIDGET_SLIDER, pos);
	
	if(!cache.exists(id))
	{
		WidgetStyleState dummy;
		dummy.blend = 0.0f;
		cache.set(dummy, id);
	}
	
	float& blend_modifier = cache.at(id).blend;
	
	if(mouse_over)
	{
		blend_modifier += Timer::getTimeScale() * 5.0f;
		if(blend_modifier > 1.0f)
			blend_modifier = 1.0f;
	}
	else
	{
		blend_modifier -= Timer::getTimeScale()* 5.0f;
		if(blend_modifier < 0.0f)
			blend_modifier = 0.0f;
	}

	sprite_batch->draw(desc.texture, layer+1, desc.src_slider, pos);

	Vector2 knob_pos(pos.x + (desc.src_slider.width - desc.src_slider_knob_down.width) * state, pos.y);

	sprite_batch->draw(desc.texture, layer+2, desc.src_slider_knob_down, knob_pos, Color(255, 255, 255, int(blend_modifier * 255.0f)));
	sprite_batch->draw(desc.texture, layer+3, desc.src_slider_knob_up, knob_pos, Color(255, 255, 255, int(255.0f - blend_modifier * 255.0f)));
}

void DefaultGUIStyle::drawSelector(const Vector2& pos, const std::vector<std::string>& selections, uint item, 
		bool mouse_over, bool mouse_over_prev, bool mouse_over_next, bool pressed)
{
	uint id = GUI::hashWidget(GUI::WIDGET_SELECTOR, pos);

	if(!cache.exists(id))
	{
		SelectorStyleState dummy;
		WidgetStyleState dummy2;
		dummy.prev_blend = dummy.next_blend = 0.0f;
		dummy2.selector = dummy;
		cache.set(dummy2, id);
	}

	SelectorStyleState& state = cache.at(id).selector;

	if(mouse_over_prev)
	{
		state.prev_blend += Timer::getTimeScale() * 5.0f;
		if(state.prev_blend > 1.0f)
			state.prev_blend  = 1.0f;
	}
	else
	{
		state.prev_blend -= Timer::getTimeScale() * 5.0f;
		if(state.prev_blend < 0.0f)
			state.prev_blend = 0.0f;
	}

	if(mouse_over_next)
	{
		state.next_blend += Timer::getTimeScale() * 5.0f;
		if(state.next_blend > 1.0f)
			state.next_blend  = 1.0f;
	}
	else
	{
		state.next_blend -= Timer::getTimeScale() * 5.0f;
		if(state.next_blend < 0.0f)
			state.next_blend = 0.0f;
	}

	sprite_batch->draw(desc.texture, layer, desc.src_selector, pos, Color(255, 255, 255, 255));
	
	sprite_batch->draw(desc.texture, layer+1, desc.src_selector_prev_up, pos, Color(255, 255, 255, 255));
	sprite_batch->draw(desc.texture, layer+2, desc.src_selector_prev_down, pos, Color(255, 255, 255, int(state.prev_blend * 255.0f)));

	float shift = desc.src_selector.width - desc.src_selector_prev_up.width;
	Vector2 new_pos = pos;
	new_pos.x += shift;

	sprite_batch->draw(desc.texture, layer+1, desc.src_selector_next_up, new_pos, Color(255, 255, 255, 255));
	sprite_batch->draw(desc.texture, layer+2, desc.src_selector_next_down, new_pos, Color(255, 255, 255, int(state.next_blend * 255.0f)));

	float text_width = desc.font->getWidth(selections[item]);
	float text_height = desc.font->getHeight();

	Vector2 text_pos(ceilf((desc.src_selector.width - text_width) / 2.0f), ceilf((desc.src_selector.height - text_height) / 2.0f));
	text_pos += pos;

	desc.font->draw(text_pos, selections[item], desc.text_color);
}


void DefaultGUIStyle::endFrame()
{
	sprite_batch->draw(desc.texture, layer+5, desc.src_pointer, mouse_pos);
}

bool DefaultGUIStyle::validateDesc()
{
	// Button up & down sizes must match
	if(desc.src_button_up.size() != desc.src_button_down.size())
		return false;

	// Switch up & down sizes must match
	if(desc.src_switch_up.size() != desc.src_button_down.size())
		return false;

	// Knob up & down sizes must match
	if(desc.src_slider_knob_up.size() != desc.src_slider_knob_down.size())
		return false;

	// Knob & slider height must match
	if(abs(desc.src_slider.height) != abs(desc.src_slider_knob_down.height))
		return false;

	// Selector next and prev, up and down sizes must match
	if(desc.src_selector_next_down.size() != desc.src_selector_next_up.size())
		return false;
	if(desc.src_selector_prev_down.size() != desc.src_selector_prev_up.size())
		return false;
	if(desc.src_selector_next_up.size() != desc.src_selector_prev_up.size())
		return false;

	// Selector buttons and background height must match
	if(abs(desc.src_selector_next_down.height) != abs(desc.src_selector.height))
		return false;

	return true;
}

} // namespace